;(function() {
  if (window.NZOCJSBridge) { return }
  var messagingIframe
  var sendMessageQueue = []
  var receiveMessageQueue = []
  var messageHandlers = {}
  
  var CUSTOM_PROTOCOL_SCHEME = 'nzjsscheme'
  var QUEUE_HAS_MESSAGE = '__NZJS_QUEUE_MESSAGE__'
  
  var responseCallbacks = {}
  var uniqueId = 1
  
  function _createQueueReadyIframe(doc) {
		messagingIframe = doc.createElement('iframe')
		messagingIframe.style.display = 'none'
		messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
		doc.documentElement.appendChild(messagingIframe)
  }
  
  function init(messageHandler) {
		if (NZOCJSBridge._messageHandler) { throw new Error('NZOCJSBridge.init called twice') }
		NZOCJSBridge._messageHandler = messageHandler
		var receivedMessages = receiveMessageQueue
		receiveMessageQueue = null
		for (var i=0; i<receivedMessages.length; i++) {
      _dispatchMessageFromObjC(receivedMessages[i])
		}
  }
  
  function send(data, responseCallback) {
		_doSend({ data:data }, responseCallback)
  }
  
  function callHandler(handlerName, data, responseCallback) {
		_doSend({ handlerName:handlerName, data:data }, responseCallback)
  }
  
  function _doSend(message, responseCallback) {
		if (responseCallback) {
  var callbackId = 'cb_'+(uniqueId++)+'_'+new Date().getTime()
  responseCallbacks[callbackId] = responseCallback
  message['callbackId'] = callbackId
		}
		sendMessageQueue.push(message)
		messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
  }
  
  function _fetchQueue() {
		var messageQueueString = JSON.stringify(sendMessageQueue)
		sendMessageQueue = []
		return messageQueueString
  }
  
  function _dispatchMessageFromObjC(messageJSON) {
		setTimeout(function _timeoutDispatchMessageFromObjC() {
                   var message = JSON.parse(messageJSON)
                   var messageHandler
                   var responseCallback
                   
                   if (message.responseId) {
                   responseCallback = responseCallbacks[message.responseId]
                   if (!responseCallback) { return; }
                   responseCallback(message.responseData)
                   delete responseCallbacks[message.responseId]
                   } else {
                   if (message.callbackId) {
                   var callbackResponseId = message.callbackId
                   responseCallback = function(responseData) {
                   _doSend({ responseId:callbackResponseId, responseData:responseData })
                   }
                   }
                   
                   var handler = NZOCJSBridge._messageHandler
                   if (message.handlerName) {
                   handler = messageHandlers[message.handlerName]
                   }
                   
                   try {
                   handler(message.data, responseCallback)
                   } catch(exception) {
                   if (typeof console != 'undefined') {
                   console.log("NZOCJSBridge: WARNING: javascript handler threw.", message, exception)
                   }
                   }
                   }
                   })
  }
  
  function _handleMessageFromObjC(messageJSON) {
		if (receiveMessageQueue) {
  receiveMessageQueue.push(messageJSON)
		} else {
  _dispatchMessageFromObjC(messageJSON)
		}
  }
  
  window.NZOCJSBridge = {
		init: init,
		send: send,
		callHandler: callHandler,
		_fetchQueue: _fetchQueue,
		_handleMessageFromObjC: _handleMessageFromObjC
  }
  
  var doc = document
  _createQueueReadyIframe(doc)
  var readyEvent = doc.createEvent('Events')
  readyEvent.initEvent('NZOCJSBridgeReady')
  readyEvent.bridge = NZOCJSBridge
  doc.dispatchEvent(readyEvent)
  })();